#include "ad7689.h"
#include "math.h"

uint8_t ad7689_RxBuffer[2];
uint8_t ad7689_RxResult[2];

AD7689_CFGTypeDef AD7689_Get_Default_CFG(void)
{
  AD7689_CFGTypeDef ad7689_cfg = 
  {
    .CFG = AD7689_CFG_OVERWRITE,
    .INCC = AD7689_INCC_UNIPOLAR_GND,
    .INx = AD7689_INx_0,
    .BW = AD7689_BW_FULL,
    .REF = AD7689_REF_EXT_BUFFER,
    .SEQ = AD7689_SEQ_DISABLE,
    .RB = AD7689_RB_DISABLE
  };
  return ad7689_cfg;
}

uint16_t AD7689_Generate_CFG(AD7689_CFGTypeDef *pAD7689_CFG)
{
  uint16_t tmpCFG = 0;
  tmpCFG |= (pAD7689_CFG->CFG << 13 | pAD7689_CFG->INCC << 10 | pAD7689_CFG->INx << 7); 
  tmpCFG |= (pAD7689_CFG->BW  << 6 | pAD7689_CFG->REF << 3 | pAD7689_CFG->SEQ << 1 | pAD7689_CFG->RB);
  return tmpCFG << 2;
}

/**
 * @brief 读取AD7689指定通道的数据
 *  由于AD7689本身有的时序，以及MCU的DMA读取会产生一个周期的延迟
 *  当前返回的数据是n-3个时钟周期的数据
 */
uint16_t AD7689_Read_SPI(AD7689_CFGTypeDef* pAD7689_CFG)
{
  uint16_t tmpCFG = AD7689_Generate_CFG(pAD7689_CFG);
  uint8_t ad7689_TxBuffer[2] = {(uint8_t)(tmpCFG >> 8), (uint8_t)tmpCFG};

  HAL_GPIO_WritePin(AD7689_NSS_GPIO_Port, AD7689_NSS_Pin, GPIO_PIN_RESET);
  HAL_SPI_TransmitReceive_DMA(&hspi2, ad7689_TxBuffer, ad7689_RxBuffer, sizeof(ad7689_TxBuffer));
  return ad7689_RxResult[0] << 8 | ad7689_RxResult[1];
}

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
  if(hspi->Instance == SPI2) //AD7689 SPI Interface
  {
    HAL_GPIO_WritePin(AD7689_NSS_GPIO_Port, AD7689_NSS_Pin, GPIO_PIN_SET);
    memcpy(ad7689_RxResult, ad7689_RxBuffer, sizeof(ad7689_RxBuffer));
  }
}

/**
 * 将ADC值转换为NTC热敏电阻的温度 
 * 高侧分压电阻 10K Ohm 0.1%
 * SDNT2012X103F3950FTF 10K Ohm 1%
 * @param adc_value ADC转换后的电阻比值，范围0-65535
 * @return 温度（单位：摄氏度）
 */
float NTC_Temperature_Convert(uint16_t adc_value)
{
  // 在25°C到50°C范围内的B值
  const uint16_t b_25_50 = 3950;
  // 在25°C到80°C范围内的B值
  const uint16_t b_25_80 = 3950;
  
  // 计算电阻比值 k = R / R0
  double k = (double) adc_value / (double) (0xffff - adc_value);

  // 根据B值和电阻比值计算温度，初始假设温度在25°C到50°C之间
  double T = (double) (b_25_50 * 298.15) / (double) (298.15 * log(k) + b_25_50);

  // 如果计算的温度超过50°C，使用25°C到80°C的B值重新计算温度
  if (T > 323.15) T = (b_25_80 * 298.15) / (298.15 * log(k) + b_25_80);
  
  // 返回计算得到的温度值
  return T - 273.15;
}

/**
 * @brief 计算与加热通道绑定的温度通道的平均温度。
 * 
 * @param channel        指定加热通道的索引号，（一共有4个加热通道，序号从0开始）。
 * @param channel_config 加热通道与温度通道绑定关系的位字段，每个8位（一共4个8位）代表一个加热通道通道与温度通道的绑定关系。
 *                       - 高8位（最左侧）表示高编号的加热通道，低8位（最右侧）表示低编号的加热通道。
 *                       - 8位中的每一位代表一个温度通道是否与当前加热通道关联：
 *                         1 表示关联，0 表示未关联。
 *                         例如：0x00005A00 = 00000000 00000000 01011010 00000000
 *                         表示第 1、3、4、6 温度通道与1号加热通道绑定。
 * @param temp_now       存储8个温度通道当前温度的数组。
 *                       temp_now[0]~temp_now[7] 分别对应子通道的温度值。
 * @return float         返回与加热通道绑定的温度通道的平均温度。
 * 
 * @note 处理思路：
 * 1. 根据传入的 `channel` 号，通过右移操作提取对应的加热通道的配置字节（8位）。
 * 2. 遍历8个子通道，对启用的子通道进行温度累加，并统计启用的子通道数量。
 * 3. 返回这些启用子通道的平均温度。
 */
float ChannelLinked_Average_Temperature(uint32_t heat_channel, uint32_t channel_config, float * temp_now)
{
  // 通过右移操作提取指定加热通道的配置字节（每8位代表一个加热通道）
  for (uint32_t i = 0; i < heat_channel; i++)
  {
    channel_config  = channel_config >> 8;
  }
  channel_config = channel_config & 0xff; // 取出当前加热通道的8位配置

  // 遍历8温度通道，对启用的温度通道进行温度累加，并统计启用的温度通道数量
  int temp_channel_count = 0;
  float avg_temp = 0.0;
  for (int i = 0; i < 8; i++)
  {
    if (channel_config & 0x01)
    {
      temp_channel_count++;
      avg_temp += temp_now[i];
    }
    channel_config = channel_config >> 1;
  }

  // 计算平均温度，防止除数为0
  if (temp_channel_count != 0)
  {
    avg_temp = avg_temp / (float)temp_channel_count;
  } 

  return avg_temp;
}

void Temperature_PID_Init(Temperature_PIDTypeDef* controller, int reset)
{
  if (reset != 0)
  {
    controller->sum_error = 0.0;
    controller->pre_error = 0.0;
  }

  if (controller->delta_t == 0.0)
  {
    controller->delta_t = 0.00000001f;
  }
  controller->A[0] = controller->Kp;
  controller->A[1] = controller->Ki * controller->delta_t;
  controller->A[2] = controller->Kd / controller->delta_t;
}


float Temperature_PID(Temperature_PIDTypeDef* controller, float error, int constrain_output)
{
  controller->sum_error += error;

  float output = 0.0;

  output = (controller->A[0] * error) +
    (controller->A[1] * controller->sum_error) + 
    (controller->A[2] * (error - controller->pre_error));
  
  controller->pre_error = error;

  if (constrain_output != 0)
  {
    if (output > controller->output_max)
    {
      output = controller->output_max;
      controller->sum_error -= error;
    }
    if (output < controller->output_min)
    {
      output = controller->output_min;
      controller->sum_error -= error;
    }
  }

  return output;
}